<?php

App::uses('AppModel', 'Model');

/**
 * User Model
 *
 * @property Group $Group
 * @property Article $Article
 * @property SendLog $SendLog
 * @property Subscription $Subscription
 */
class User extends AppModel {

    /**
     * Display field
     *
     * @var string
     */
    public $displayField = 'name';
    public $actsAs = array('Acl' => array('type' => 'requester'));
    /**
     * belongsTo associations
     *
     * @var array
     */
    public $belongsTo = array(
        'Group' => array(
            'className' => 'Group',
            'foreignKey' => 'group_id',
            'conditions' => '',
            'fields' => '',
            'order' => ''
        )
    );
    /**
     * hasMany associations
     *
     * @var array
     */
    public $hasMany = array(
        'Article' => array(
            'className' => 'Article',
            'foreignKey' => 'user_id',
            'dependent' => false,
            'conditions' => '',
            'fields' => '',
            'order' => '',
            'limit' => '',
            'offset' => '',
            'exclusive' => '',
            'finderQuery' => '',
            'counterQuery' => ''
        ),
        'SendLog' => array(
            'className' => 'SendLog',
            'foreignKey' => 'user_id',
            'dependent' => false,
            'conditions' => '',
            'fields' => '',
            'order' => '',
            'limit' => '',
            'offset' => '',
            'exclusive' => '',
            'finderQuery' => '',
            'counterQuery' => ''
        ),
        'Subscription' => array(
            'className' => 'Subscription',
            'foreignKey' => 'user_id',
            'dependent' => false,
            'conditions' => '',
            'fields' => '',
            'order' => '',
            'limit' => '',
            'offset' => '',
            'exclusive' => '',
            'finderQuery' => '',
            'counterQuery' => ''
        )
    );
    /**
     * Virtual fields
     * 
     * @var array
     */
     public $virtualFields = array (
                'name' => "CONCAT(IFNULL(User.first_name,''), ' ', IFNULL(User.last_name,''))",
                
            );
        
    public function beforeSave() {
        if (isset($this->data[$this->alias]['password'])) {
            $this->data[$this->alias]['password'] = AuthComponent::password($this->data[$this->alias]['password']);
        }
        return true;
    }

    public function parentNode() {
        if (!$this->id && empty($this->data)) {
            return null;
        }
        if (isset($this->data['User']['group_id'])) {
            $groupId = $this->data['User']['group_id'];
        } else {
            $groupId = $this->field('group_id');
        }
        if (!$groupId) {
            return null;
        } else {
            return array('Group' => array('id' => $groupId));
        }
    }
    /**
 * Custom validation method to ensure that the two entered passwords match
 *
 * @param string $password Password
 * @return boolean Success
 */
	public function confirmPassword($password = null) {
		if ((isset($this->data[$this->alias]['password']) && isset($password['temppassword']))
			&& !empty($password['temppassword'])
			&& ($this->data[$this->alias]['password'] === $password['temppassword'])) {
			return true;
		}
		return false;
	}

    /**
 * Registers a new user
 *
 * Options:
 * - bool emailVerification : Default is true, generates the token for email verification
 * - bool removeExpiredRegistrations : Default is true, removes expired registrations to do cleanup when no cron is configured for that
 * - bool returnData : Default is true, if false the method returns true/false the data is always available through $this->User->data
 *
 * @param array $postData Post data from controller
 * @param mixed should be array now but can be boolean for emailVerification because of backward compatibility
 * @return mixed
 */
	public function register($postData = array(), $options = array()) {
		if (is_bool($options)) {
			$options = array('emailVerification' => $options);
		}

		$defaults = array(
			'emailVerification' => true,
			'removeExpiredRegistrations' => true,
			'returnData' => true);
		extract(array_merge($defaults, $options));

		$postData = $this->_beforeRegistration($postData, $emailVerification);

		if ($removeExpiredRegistrations) {
			$this->_removeExpiredRegistrations();
		}

		$this->set($postData);
		if ($this->validates()) {
			
			$this->create();
			$this->data = $this->save($postData, false);
			$this->data[$this->alias]['id'] = $this->id;
			if ($returnData) {
				return $this->data;
			}
			return true;
		}
		return false;
	}
/**
 * Verifies a users email by a token that was sent to him via email and flags the user record as active
 *
 * @param string $token The token that wa sent to the user
 * @return array On success it returns the user data record
 */
	public function verifyEmail($token = null) {
		$user = $this->find('first', array(
			'contain' => array(),
			'conditions' => array(
				$this->alias . '.email_verified' => 0,
				$this->alias . '.email_token' => $token),
			'fields' => array(
				'id', 'email', 'email_token_expires', 'role')));

		if (empty($user)) {
			throw new RuntimeException(__d('users', 'Invalid token, please check the email you were sent, and retry the verification link.'));
		}

		$expires = strtotime($user[$this->alias]['email_token_expires']);
                
		if ($expires < time()) {
			throw new RuntimeException(__d('users', 'The token has expired.'));
		}

		$data[$this->alias]['active'] = 1;
		$user[$this->alias]['email_verified'] = 1;
		$user[$this->alias]['email_token'] = null;
		$user[$this->alias]['email_token_expires'] = null;

		$user = $this->save($user, array(
			'validate' => false,
			'callbacks' => false));
		$this->data = $user;
		return $user;
	}
 /**
 * Checks the token for a password change
 * 
 * @param string $token Token
 * @return mixed False or user data as array
 */
	public function checkPasswordToken($token = null) {
		$user = $this->find('first', array(
			'contain' => array(),
			'conditions' => array(
				$this->alias . '.active' => 1,
				$this->alias . '.password_token' => $token,
				$this->alias . '.email_token_expires >=' => date('Y-m-d H:i:s'))));
		if (empty($user)) {
			return false;
		}
		return $user;
	}
        /**
 * Resets the password
 * 
 * @param array $postData Post data from controller
 * @return boolean True on success
 */
	public function resetPassword($postData = array()) {
		$result = false;

		$tmp = $this->validate;
		$this->validate = array(
			'new_password' => $tmp['password'],
			'confirm_password' => array(
				'required' => array(
					'rule' => array('compareFields', 'new_password', 'confirm_password'), 
					'message' => __d('users', 'The passwords are not equal.'))));

		$this->set($postData);
		if ($this->validates()) {
			$this->data[$this->alias]['password'] = AuthComponent::password($this->data[$this->alias]['new_password']);
			$this->data[$this->alias]['password_token'] = null;
			$result = $this->save($this->data, array(
				'validate' => false,
				'callbacks' => false));
		}

		$this->validate = $tmp;
		return $result;
	}


 /**
 * Checks if an email is in the system, validated and if the user is active so that the user is allowed to reste his password
 *
 * @param array $postData post data from controller
 * @return mixed False or user data as array on success
 */
	public function passwordReset($postData = array()) {
		$this->recursive = -1;
		$user = $this->find('first', array(
			'conditions' => array(
				$this->alias . '.active' => 1,
				$this->alias . '.email' => $postData[$this->alias]['email'])));

		if (!empty($user) && $user[$this->alias]['email_verified'] == 1) {
			$sixtyMins = time() + 43000;
			$token = $this->generateToken();
			$user[$this->alias]['password_token'] = $token;
			$user[$this->alias]['email_token_expires'] = date('Y-m-d H:i:s', $sixtyMins);
			$user = $this->save($user, false);
			$this->data = $user;
			return $user;
		} elseif (!empty($user) && $user[$this->alias]['email_verified'] == 0){
			$this->invalidate('email', __d('users', 'This Email Address exists but was never validated.'));
		} else {
			$this->invalidate('email', __d('users', 'This Email Address does not exist in the system.'));
		}

		return false;
	}


/**
 * Optional data manipulation before the registration record is saved
 *
 * @param array post data array
 * @param boolean Use email generation, create token, default true
 * @return array
 */
	protected function _beforeRegistration($postData = array(), $useEmailVerification = true) {
		if ($useEmailVerification == true) {
			$postData[$this->alias]['email_token'] = $this->generateToken();
			$postData[$this->alias]['email_token_expires'] = date('Y-m-d H:i:s', time() + 86400);
		} else {
			$postData[$this->alias]['email_verified'] = 1;
		}
		$postData[$this->alias]['active'] = 1;
		return $postData;
	}
/**
 * Generate token used by the user registration system
 *
 * @param int $length Token Length
 * @return string
 */
	public function generateToken($length = 10) {
		$possible = '0123456789abcdefghijklmnopqrstuvwxyz';
		$token = "";
		$i = 0;

		while ($i < $length) {
			$char = substr($possible, mt_rand(0, strlen($possible) - 1), 1);
			if (!stristr($token, $char)) {
				$token .= $char;
				$i++;
			}
		}
		return $token;
	}

 /**
 * Removes all users from the user table that are outdated
 *
 * Override it as needed for your specific project
 *
 * @return void
 */
	protected function _removeExpiredRegistrations() {
		$this->deleteAll(array(
			$this->alias . '.email_verified' => 0,
			$this->alias . '.email_token_expires <' => date('Y-m-d H:i:s')));
	}


    /**
     * Validate of user field
     */
    protected function _validationRules() {

        $this->validate = array(
            'password' => array(
                array(
                    'rule' => array('minLength', '6'),
                    'message' => __('Mimimum 6 characters long', true)
                ),
                array(
                    'rule' => 'notEmpty',
                    'message' => __('Password is required.', true)
                )
            ),
            'username' => array(
                array(
                    'rule' => 'notEmpty',
                    'message' => __('Username is required.', true)
                ),
                array(
                    'rule' => 'isUnique',
                    'message' => __('User already exist.', true)
                )
            ),
            'email' => array(
                array(
                    'rule' => 'email',
                    'message' => __('Please supply a valid email address.', true)
                ),
                array(
                    'rule' => 'isUnique',
                    'message' => __('Email already exist.', true)
                )
            ),
            'confirm_email' => array(
                array(
                    'rule' => 'email',
                    'message' => __('Please supply a valid email address.', true)
                )                
            ),
            'first_name' => array(
                array(
                    'rule' => 'notEmpty',
                    'message' => __('First name is required.', true)
                )
            ),
            'last_name' => array(
                array(
                    'rule' => 'notEmpty',
                    'message' => __('Last name is required.', true)
                )
            ),
            'phone' => array(
                array(
                    'rule' => 'notEmpty',
                    'message' => __('Phone is required.', true)
                )
            ),
            'house_number' => array(
                array(
                    'rule' => 'notEmpty',
                    'message' => __('House number is required.', true)
                )
            ),
            'street' => array(
                array(
                    'rule' => 'notEmpty',
                    'message' => __('Street is required.', true)
                )
            ),
            'city' => array(
                array(
                    'rule' => 'notEmpty',
                    'message' => __('City or Province is required.', true)
                )
            )
        );
    }
/**
 * Setup validation rules
 *
 * @return void
 */
	protected function _setupValidation() {
		$this->validatePasswordChange = array(
			'new_password' => $this->validate['password'],
			'confirm_password' => array(
				'required' => array('rule' => array('compareFields', 'new_password', 'confirm_password'), 'required' => true, 'message' => __d('users', 'The passwords are not equal.'))),
			'old_password' => array(
				'to_short' => array('rule' => 'validateOldPassword', 'required' => true, 'message' => __d('users', 'Invalid password.'))));
	}
/**
 * Validation method to check the old password
 *
 * @param array $password 
 * @return boolean True on success
 */
	public function validateOldPassword($password) {
		if (!isset($this->data[$this->alias]['id']) || empty($this->data[$this->alias]['id'])) {
			if (Configure::read('debug') > 0) {
				throw new OutOfBoundsException(__d('users', '$this->data[\'' . $this->alias . '\'][\'id\'] has to be set and not empty'));
			}
		}

		$currentPassword = $this->field('password', array($this->alias . '.id' => $this->data[$this->alias]['id']));
		return $currentPassword === Security::hash($password['old_password'], null, true);
	}

/**
 * Validation method to compare two fields
 *
 * @param mixed $field1 Array or string, if array the first key is used as fieldname
 * @param string $field2 Second fieldname
 * @return boolean True on success
 */
	public function compareFields($field1, $field2) {
		if (is_array($field1)) {
			$field1 = key($field1);
		}
		if (isset($this->data[$this->alias][$field1]) && isset($this->data[$this->alias][$field2]) && 
			$this->data[$this->alias][$field1] == $this->data[$this->alias][$field2]) {
			return true;
		}
		return false;
	}
        public function activeUser($id) {
            $data['User'] = array(
                'email_verified' => 1,
                'email_token' => null,
                'active' => 1
            );
            $this->id = $id;
            if ($this->exists()) {
                if ($this->save($data)) {
                    return true;
                }
            }
            return false;
        }
        public function getSubscribers() {
            $subscribers = $this->Subscription->find('all', array(
                'conditions' => array('Subscription.active' => 1),
                'contain' => array('User')
            ));
            return $subscribers;
        }
        public function getAllActiveSubscribers() {
            $subscribers = $this->Subscription->find('all', array( 
                'conditions' => array('Subscription.active' => 1),
                'contain' => array('User')
            ));
            return $subscribers;
        }
        public function getLastSubscribers($limit = 1) {
            $subscribers = $this->Subscription->find('all', array(                
                'contain' => array('User'),
                'limit' => $limit,
                'order' => array('Subscription.id' => 'DESC')
            ));
            return $subscribers;
        }
        public function getTotalArticleForSubscribers() {
            $subscribers = $this->Article->find('all', array(
                'conditions' => array('Article.type' => ARTICLE_TYPE_ARTICLE),
                'contain' => array('User')
            ));
            return $subscribers;
        }
        public function getSubscribersBeforeExpire() {//15 days before expire
            $subscribers = $this->Subscription->find('all', array(
                'conditions' => array('Subscription.active' => 1,'DATEDIFF(DATE(Subscription.expire_alert),DATE(NOW())) <=15 && DATEDIFF(DATE(Subscription.expire_alert),DATE(NOW())) >= 0'),
                'contain' => array('User')
            ));
            return $subscribers;
        }
}
